import datetime

from info.models import User
from . import passport_blue
from flask import request, abort, make_response, jsonify, session, render_template
from info import redis_store, constants, response_code, db
from info.utils.captcha.captcha import captcha
import logging, re, random
from info.libs.yuntongxun.sms import CCP


@passport_blue.route('/sms_code', methods=['POST'])
def send_sms_code():
    """发送短信验证码
    1.接受参数：mobile, image_code, image_code_id
    2.校验参数：判断参数是否齐全，判断手机号格式是否正确
    3.使用image_code_id从redis中读取出服务器存储的图片验证码
    4.使用用户输入的图片验证码 对比 服务器存储的图片验证码
    5.如果对比成功生成短信验证码，长度是6位的随机数字
    6.再使用CCP()单例发送短信验证码
    7.发送短信验证码成功，就把短信验证码存储到redis()
    8.返回短信验证码发送的结果
    """
    # 1 接收参数：mobile  image_code  image_code_id
    json_dict = request.json
    mobile = json_dict.get('mobile')
    image_code_client = json_dict.get('image_code')
    image_code_id = json_dict.get('image_code_id')

    # 2.校验参数：判断参数是否齐全，判断手机号格式是否正确
    if not all([mobile, image_code_client, image_code_id]):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='缺少参数')

    # 用正则匹配 验证手机号是否填写正确  ^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$
    if not re.match('^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$', mobile):
        return jsonify(errno=response_code.RET.DATAERR, errmsg='手机号格式错误')

    # 3.使用image_code_id从redis中读取出服务器存储的图片验证码
    try:
        image_code_server = redis_store.get('ImageCode:' + image_code_id)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.DBERR, errmsg='查询图片验证码失败')
    # 判断 image_code_server 是否存在
    if not image_code_server:
        return jsonify(errno=response_code.RET.NODATA, errmsg='图片验证码不存在')
    # 4.使用用户输入的图片验证码 对比 服务器存储的图片验证码
    if image_code_client.lower() != image_code_server.lower():
        return jsonify(errno=response_code.RET.DATAERR, errmsg='图片验证码输入错误')

    # 5.如果对比成功生成短信验证码，长度是6位的随机数字
    sms_code = '%06d' % random.randint(0, 999999)
    logging.debug(sms_code)

    # 6.再使用CCP()单例发送短信验证码    - (手机号,[短信验证码,过期时间],模板号)
    result = CCP().send_sms_code(mobile, [sms_code, 5], 1)
    if result != 0:
        return jsonify(errno=response_code.RET.THIRDERR, errmsg='短信发送失败')
    # 7.发送短信验证码成功，就把短信验证码存储到redis()
    try:
        redis_store.set('SMS:' + mobile, sms_code, constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.OK, errmsg='存储短信失败')

    # 8 返回发送短信的结果
    return jsonify(errno=response_code.RET.OK, errmsg='发送短信成功')






@passport_blue.route('/image_code',methods=['GET'])
def get_image_code():
    """提供图片验证码
    1.接受参数：imageCodeId (UUID,图片验证码唯一标识符)
    2.校验参数：必须传入imageCodeId
    3.生成图片验证码的文字和图片信息
    4.将imageCodeId和图片验证码文字绑定到redis
    5.响应图片验证码的图片信息给用户
    """

    # 1 接收参数： imageCodeId (UUID, 图片验证码唯一标识符)
    image_code_id = request.args.get('imageCodeId')

    # 2.校验参数：必须传入imageCodeId
    if not image_code_id:
        abort(400)  # 缺少参数


    # 生成图片验证码的文字和图片信息 text 是图片验证码的文字， image 是图片验证码的图片
    # 需要在虚拟环境中安装 Pillow模块
    name, text, image = captcha.generate_captcha()

    # 4.将imageCodeId和图片验证码文字绑定到redis:图片验证码五分钟之后自动的过期
    try:
        redis_store.set('ImageCode:' + image_code_id, text, constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        logging.error(e)
        abort(500)  # 表示服务器错误

    # 5 响应图片验证码的图片信息给用户
    # 将 image 作为响应体
    response = make_response(image)

    # 指定响应体的数据类型
    response.headers['Content-Type'] = 'image/jpg'
    return response


@passport_blue.route('/register', methods=["POST"])
def register():
    """
    注册
    1. 获取参数和判断是否有值
    2. 从redis中获取指定手机号对应的短信验证码的
    3. 校验验证码
    4. 初始化 user 模型，并设置数据并添加到数据库
    5. 保存当前用户的状态
    6. 返回注册的结果
    :return:
    """

    # 1 接收参数： mobile  smscode  password(明文)
    json_dict = request.json
    mobile = json_dict.get('mobile')
    sms_code_client = json_dict.get('smscode')
    password = json_dict.get('password')


    #  2 校验参数, 参数是否齐全，校验手机号码格式是否正确
    if not all([mobile, sms_code_client, password]):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='缺少必传参数')
    if not re.match('^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$', mobile):
        return jsonify(errno=response_code.RET.DATAERR, errmsg='手机号格式错误')


    # 3 使用mobile 读取redis 中的短信验证码
    try:
        sms_code_server = redis_store.get('SMS:' + mobile)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.DBERR, errmsg='查询短信验证码失败')
    if not sms_code_server:
        return jsonify(errno=response_code.RET.NODATA, errmsg='短信验证码不存在')


    # 4 使用客户端传入的短信验证码和服务器中保存的短信验证码进行对比
    if sms_code_client != sms_code_server:
        return jsonify(errno=response_code.RET.DATAERR, errmsg='短信验证码输入错误')



    # 5 创建User 模型对象, 并给用户属性赋值
    user = User()
    user.nick_name = mobile    # 默认手机号就是用户名
    user.mobile = mobile


    # user.password_hash = password  ---  password 是明文,而password_hash 需要存储密文
    # password : 是即将给User 模型类增加的属性(Python是面向对象的高级编程语言，可以在代码运行的过程中给对象添加属性)
    # 在 password 属性的setter 方法中，拿着明文密码进行加密处理，再保存到paaword_hash中
    user.password = password

    # 6 记录保存最后一次登录的时间(last_login, 为了实现注册即登录)
    user.last_login = datetime.datetime.now()

    # 7 需要将用户注册数据保存到Mysql 数据库中
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=response_code.RET.DATAERR, errmsg='存储用户数据失败')


    # 8 将状态保持信息写入到session(为了实现注册即登录)
    session['user_id'] = user.id
    session['user_mobile'] = user.mobile

    # 返回注册结果
    return jsonify(errno=response_code.RET.OK, errmsg='注册成功')


@passport_blue.route('/login',methods=['POST'])
def login():
    """登录
    1.接受参数：mobile, password(明文)
    2.校验参数：参数是否齐全，校验手机号码格式是否正确
    3.使用mobile查询user是否存在
    4.如果user存在，再去校验密码
    5.如果密码校验通过，就记录最后一次登录的时间和状态保持
    6.响应登录结果
    """
    # 1 接受参数  mobile   password(明文)
    json_dict = request.json
    mobile = json_dict.get('mobile')
    password = json_dict.get('password')

    # 2 校验参数：参数是否齐全，校验手机号码格式是否正确
    if not all([mobile, password]):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='缺少必传参数')
    if not re.match('^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$', mobile):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='手机号格式错误')

    # 3 使用mobile 查询user是否存在
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=response_code.RET.DBERR, errmsg='用户名或密码错误')
    if not user:
        return jsonify(errno=response_code.RET.NODATA, errmsg='用户名或密码错误')

    # 4 如果user存在，校验密码
    if not user.check_password(password):
        return jsonify(errno=response_code.RET.PARAMERR, errmsg='用户名或密码错误')



    # 5 如果密码校验通过，就记录最后一次登录的时间和状态保持
    user.last_login = datetime.datetime.now()

    try:
        db.session.commit()   # 修改不需要 add
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=response_code.RET.DATAERR, errmsg='登录失败')


    # 状态保持
    session['user_id'] = user.id
    session['nick_name'] = user.nick_name
    session['mobile'] = user.mobile
    # 如果是管理员账号进入前台页面，状态保持中需要有is_admin
    if user.is_admin:
        session['is_admin'] = True

    # 6 响应登录结果
    return jsonify(errno=response_code.RET.OK, errmsg='登录成功')



@passport_blue.route('/logout')
def logout():
    """退出登录
    1.只需要将状态保持信息删除即可(退出登录的方案有很多，但是核心的思想是清除session)
    2.返回退出登录的结果
    """
    session.pop('user_id', None)
    session.pop('nick_name', None)
    session.pop('mobile', None)

    # 如果是管理员登录前台时，退出时，需要将is_admin 清除
    session.pop('is_admin', False)

    return jsonify(errno=response_code.RET.OK, errmsg='退出登录成功')


